define(['knockout', 'toastr', 'config'], function (ko, toastr, Config) {
    //config the modules
    toastr.options.closeDuration = 300;
    toastr.options.timeOut = 1500;
    toastr.options._errorTimeout = 4000;
    toastr.options.extendedTimeOut = 60;
    toastr.options.positionClass = 'toast-top-center';

    var util = {
        dicts: {
            personSex: [
                {text: "男", value: 1},
                {text: "女", value: 2}
            ]
        }
    };
    //css
    var cssUtil = {
        zico: function (icon, size, animate) {
            var css = {};

            if (icon) {
                css['zi'] = true;
                css['zi_' + icon] = true;
            }

            if (size) {
                css['zi_' + size + 'x'] = true;
            }

            if (animate) {
                css['zi_' + animate] = true;
            }

            return css;
        },
        addCssActive: function (items, firstActive) {//add the css active field to model for bootstrap ui active bind
            for (var index = 0; index < items.length; ++index) {
                if (0 == index && firstActive) {
                    items[0]._cssActive = ko.observable(true);
                } else {
                    items[index]._cssActive = ko.observable(false);
                }
            }

            return items;
        },
        changeCssActive: function (item, items) {
            if (item && !item._cssActive()) {
                items.forEach(function (itm) {
                    if (itm._cssActive()) itm._cssActive(false);
                });

                item._cssActive(true);

                return true;
            }

            return false;
        },
        findCssActive: function (items) {
            var temp = [];

            items.forEach(function (item) {
                if (item._cssActive()) temp.push(item);
            });

            return temp;
        },
        clearCssActive: function (items) {
            var tempItems = this.findCssActive(items);

            tempItems.forEach(function (item) {
                item._cssActive(false);
            });
        },
        generateBindCss: function (obj) {//convert a string or array to ko bind able type
            var css = {};

            if (obj) {
                switch (typeof obj) {
                    case 'string':
                        css[obj] = true;
                        break;
                    case 'object':
                        if (obj instanceof Array) {
                            obj.forEach(function (item) {
                                css[item] = true;
                            });
                        }
                        break;
                }
            }

            return css;
        },
        calculateSize: function (size) {
            if (typeof size == 'number') {
                return '{0}px'.formatTpl(size);
            } else {
                return size;
            }
        }
    };
    //ajax
    $.ajaxSetup({
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        xhrFields: Config.crossDomain ? {withCredentials: true} : {},
        crossDomain: Config.crossDomain,
        beforeSend: function (jqXHR, request) {
            // if(request.type != 'GET'){
            if (ko.root) {
                if (window.layer) {
                    ko.root.showLoading();
                } else {
                    ko.root.showProgress(true);
                }
            }
            // }

            if (ko.root.mobile) {
                jqXHR.setRequestHeader('device', 'mobile');
            }
        },
        dataFilter: function (data) {
            if (Config.ajaxFormatter) data = JSON.stringify(Config.ajaxFormatter(JSON.parse(data)));

            return data;
        },
        complete: function (xhr, status) {
            if (ko.root) {
                if (window.layer) {
                    ko.root.closeLoading();
                } else {
                    ko.root.showProgress(false);
                }
            }

            if (status == 'success' && xhr.responseJSON && xhr.responseJSON.success != undefined) {
                if ((xhr.responseJSON.success && this.type != 'GET') || !xhr.responseJSON.success) {
                    commonUtil.showAlert({data: xhr.responseJSON});
                }

                if (Config.ajaxComplete) Config.ajaxComplete(xhr.responseJSON);
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {
            var errorText = '';

            switch (jqXHR.status) {
                case 401:
                    errorText = '没有提供认证信息';
                    break;
                case 403:
                    errorText = '请求的资源不允许访问';
                    break;
                case 404:
                    errorText = '请求的内容不存在';
                    break;
                case 408:
                    errorText = '客户端请求超时';
                    break;
                case 500:
                    errorText = '服务器错误';
                    break;
                default:
                    errorText = '未知错误';
            }

            commonUtil.showAlert({data: jqXHR.status + ' : ' + errorText, level: 'error'});
        },
        cache: false
    });
    var ajaxUtil = {
        ajaxGet: function (/*{url:,params:,callback:}*/params) {
            $.ajax
            ({
                type: "get",
                data: params.data,
                url: params.url,
                success: function (data) {
                    params.callback(data);
                }
            });
        },
        ajaxPost: function (params) {
            $.ajax
            ({
                type: "post",
                data: (params.data instanceof Object) ? JSON.stringify(params.data) : params.data,
                url: params.url,
                success: function (data) {
                    params.callback(data);
                }
            });
        },
        ajaxPut: function (params) {
            $.ajax
            ({
                type: "put",
                data: (params.data instanceof Object) ? JSON.stringify(params.data) : params.data,
                url: params.url,
                success: function (data) {
                    params.callback(data);
                }
            });
        },
        ajaxDelete: function (params) {
            $.ajax
            ({
                type: "delete",
                data: (params.data instanceof Object) ? JSON.stringify(params.data) : params.data,
                url: params.url,
                success: function (data) {
                    params.callback(data);
                }
            });
        },
        ajaxGetSync: function (params) {
            var result = null;

            $.ajax
            ({
                type: "get",
                data: params.data,
                url: params.url,
                async: false,
                success: function (data) {
                    result = data;
                }
            });

            return result;
        },
        ajaxPostSync: function (params) {
            var result = null;

            $.ajax
            ({
                type: "post",
                url: params.url,
                data: (params.data instanceof Object) ? JSON.stringify(params.data) : params.data,
                async: false,
                success: function (data) {
                    result = data;
                }
            });

            return result;
        },
        ajaxPutSync: function (params) {
            var result = null;

            $.ajax
            ({
                type: "put",
                url: params.url,
                data: (params.data instanceof Object) ? JSON.stringify(params.data) : params.data,
                async: false,
                success: function (data) {
                    result = data;
                }
            });

            return result;
        },
        ajaxDeleteSync: function (params) {
            var result = null;

            $.ajax
            ({
                type: "delete",
                url: params.url,
                data: (params.data instanceof Object) ? JSON.stringify(params.data) : params.data,
                async: false,
                success: function (data) {
                    result = data;
                }
            });

            return result;
        },
        // ajaxForm: function (url, model, callback) {
        //     var formData = this.generateFormData(model);
        //
        //     $.ajax({
        //         url: url + (model.id() == Config.modelDefaultId ? '' : '/' + model.id()),
        //         type: model.id() == 0 ? 'POST' : 'POST',
        //         data: formData,
        //         // 告诉jQuery不要去处理发送的数据
        //         processData: false,
        //         // 告诉jQuery不要去设置Content-Type请求头
        //         contentType: false,
        //         success: function (data) {
        //             callback(data);
        //         }
        //     });
        // },
        ajaxFormPost: function (params) {
            $.ajax({
                url: params.url,
                type: 'POST',
                data: params.data,
                // 告诉jQuery不要去处理发送的数据
                processData: false,
                // 告诉jQuery不要去设置Content-Type请求头
                contentType: false,
                success: function (data) {
                    params.callback(data);
                }
            });
        },
        ajaxFormPut: function (params) {
            $.ajax({
                url: params.url,
                type: 'PUT',
                data: params.data,
                // 告诉jQuery不要去处理发送的数据
                processData: false,
                // 告诉jQuery不要去设置Content-Type请求头
                contentType: false,
                success: function (data) {
                    params.callback(data);
                }
            });
        },
        generateFormData: function (model) {
            var formData = new FormData();

            model.fields.forEach(function (field) {
                if (field.name.substr(0, 1) != '_' && model[field.name]) {
                    switch (field.type) {
                        case 'file':
                            if (model[field.name].file && model[field.name].file()) {
                                formData.append(field.name, model[field.name].file());
                            }
                            break;
                        default:
                            if (model[field.name] && model[field.name] instanceof Function) {
                                formData.append(field.name, model[field.name]());
                            }
                    }
                }
            });

            for (var key in model.extraData) {
                formData.append(key, model.extraData[key]);
            }

            return formData;
        },
        ajaxCallback: function (/*{callback:,data:,storeId:}*/params) {
            //this.showAlert(data);
            if (params.callback) params.callback(params.data);

            if (params.storeId && params.data.success) {
                var store = ko.root.storeCache[params.storeId];

                if (store) {
                    store.empty();
                    store.load();
                }
            }
        }
    };
    //component
    var componentUtil = {
        generateOperations: function (operations) {
            var tempOperations = [];

            if (operations && operations.length > 0) {
                operations.forEach(function (item, index) {
                    switch (item) {
                        case 'view':
                            tempOperations.push({name: item, css: cssUtil.zico('eye'), title: '查看'});
                            break;
                        case 'delete':
                            tempOperations.push({name: item, css: cssUtil.zico('trashalt'), title: '删除'});
                            tempOperations[tempOperations.length - 1].css['text-danger'] = true;
                            break;
                        case 'add':
                            tempOperations.push({name: item, css: cssUtil.zico('plus'), title: '添加'});
                            break;
                        case 'refresh':
                            tempOperations.push({name: item, css: cssUtil.zico('sync'), title: '刷新'});
                            break;
                        case 'edit':
                            tempOperations.push({name: item, css: cssUtil.zico('edit'), title: '编辑'});
                            break;
                        case 'query':
                            tempOperations.push({name: item, css: cssUtil.zico('search'), title: '查询'});
                            break;
                        case 'upload':
                            tempOperations.push({name: item, css: cssUtil.zico('upload'), title: '上传'});
                            break;
                        default:
                            if (item instanceof Object) {
                                item.css = item.icon ? cssUtil.zico(item.icon) : cssUtil.zico(item.name);
                                item.title = item.title ? item.title : item.name;
                                tempOperations.push(item);
                            } else {
                                tempOperations.push({name: item, css: cssUtil.zico(item), title: item});
                            }
                    }
                });
            }

            return tempOperations;
        },
        operationSwitch: function (operation, record) {
            switch (operation.name) {
                case 'view':
                case 'edit':
                    ko.root.openModal({operation: operation.name, record: record});
                    break;
                case 'delete':
                    ko.root.openModal({level: 'danger', operation: operation.name, record: record});
                    break;
                default:
            }
        },
        treeConfig: {
            checkedIcon: 'zi zi_squareLine checked',
            uncheckedIcon: 'zi zi_squareLine',
            expandIcon: 'zi zi_forRight',
            collapseIcon: 'zi zi_forDown',
            //color: '#17a2b8',
            //selectedBackColor: '#7bbfea',
            onhoverColor: '#eeeeff'
        },
        treeFormat: function (data, fun) {
            var scope = this;
            if (data instanceof Array) {
                data.forEach(function (childData) {
                    scope.treeFormat(childData, fun);
                });
            } else {
                if (fun) fun(data);
                if (data.nodes) {
                    scope.treeFormat(data.nodes, fun);
                }
            }
        }
    };
    //common
    var commonUtil = {
        showAlert: function (/*{data:,level:}*/params) {
            if (params.level) {
                if (params.data) {
                    switch (params.level) {
                        case 'success':
                            toastr.success(params.data);
                            break;
                        case 'warning':
                            toastr.warning(params.data);
                            break;
                        case 'error':
                            toastr.error(params.data, '', {timeOut: toastr.options._errorTimeout});
                            break;
                    }
                } else {
                    console.log('can not alert empty message');
                }

            } else {
                var message = undefined;

                if (params.data.message) message = params.data.message;
                if (params.data.msg) message = params.data.msg;

                if (message) {
                    if (params.data.success) {
                        toastr.success(message);
                    } else {
                        toastr.error(message, '', {timeOut: toastr.options._errorTimeout});
                    }
                } else {
                    console.log('no ajax message from server');
                }
            }
        },
        checkDevice: function () {
            var userAgentInfo = navigator.userAgent;
            var Agents = new Array("Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod");
            var flag = {device: 'PC', env: 'UNKNOWN'};

            if (!!window.ActiveXObject || "ActiveXObject" in window) {
                return {device: 'PC', env: 'IE'};
            }
            if (navigator.userAgent.indexOf("Android") != -1) {
                return {device: 'Mobile', env: 'Android'};
            }
            if (navigator.userAgent.indexOf("iPhone") != -1) {
                return {device: 'Mobile', env: 'iPhone'};
            }
            if (navigator.userAgent.indexOf("Edge") != -1) {
                return {device: 'PC', env: 'Edge'};
            }
            if (navigator.userAgent.indexOf("Chrome") != -1) {
                return {device: 'PC', env: 'Chrome'};
            }
            if (navigator.userAgent.indexOf("Firefox") != -1) {
                return {device: 'PC', env: 'Firefox'};
            }
            if (navigator.userAgent.indexOf("Safari") != -1) {
                return {device: 'PC', env: 'Safari'};
            }
            if (navigator.userAgent.indexOf("SymbianOS") != -1) {
                return {device: 'Mobile', env: 'SymbianOS'};
            }
            if (navigator.userAgent.indexOf("Windows Phone") != -1) {
                return {device: 'Mobile', env: 'Windows Phone'};
            }
            if (navigator.userAgent.indexOf("iPad") != -1) {
                return {device: 'Mobile', env: 'iPad'};
            }
            if (navigator.userAgent.indexOf("iPod") != -1) {
                return {device: 'Mobile', env: 'iPod'};
            }

            return flag;
        },
        identityCodeValid: function (code) {
            var city = {
                11: "北京",
                12: "天津",
                13: "河北",
                14: "山西",
                15: "内蒙古",
                21: "辽宁",
                22: "吉林",
                23: "黑龙江 ",
                31: "上海",
                32: "江苏",
                33: "浙江",
                34: "安徽",
                35: "福建",
                36: "江西",
                37: "山东",
                41: "河南",
                42: "湖北 ",
                43: "湖南",
                44: "广东",
                45: "广西",
                46: "海南",
                50: "重庆",
                51: "四川",
                52: "贵州",
                53: "云南",
                54: "西藏 ",
                61: "陕西",
                62: "甘肃",
                63: "青海",
                64: "宁夏",
                65: "新疆",
                71: "台湾",
                81: "香港",
                82: "澳门",
                91: "国外 "
            };
            var tip = "";
            var pass = true;

            if (!code || !/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i.test(code)) {
                tip = "身份证号格式错误";
                pass = false;
            } else if (!city[code.substr(0, 2)]) {
                tip = "身份证地址编码错误";
                pass = false;
            } else {
                //18位身份证需要验证最后一位校验位
                if (code.length == 18) {
                    code = code.toUpperCase().split('');
                    //∑(ai×Wi)(mod 11)
                    //加权因子
                    var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
                    //校验位
                    var parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2];
                    var sum = 0;
                    var ai = 0;
                    var wi = 0;
                    for (var i = 0; i < 17; i++) {
                        ai = code[i];
                        wi = factor[i];
                        sum += ai * wi;
                    }
                    var last = parity[sum % 11];
                    if (parity[sum % 11] != code[17]) {
                        tip = "身份证校验位错误";
                        pass = false;
                    }
                }
            }
            if (!pass) this.showAlert({data: tip, level: 'error'});
            return {success: pass, message: tip};
        },
        getRestUrl: function (urlRelative) {
            return Config.baseUrl + Config.restPrefix + urlRelative;
        },
        getAjaxUrl: function (urlRelative) {
            return Config.baseUrl + urlRelative;
        },
        generateUrl: function (proxy) {
            var url = this.getRestUrl(proxy.url);

            if (proxy.type == 'ajax') {
                url = this.getAjaxUrl(proxy.url);
            }

            return url;
        },
        getLocationHash: function (level) {
            var maxLevel = this.getHashMaxLevel();

            if (level > maxLevel) {
                return '';
            }

            var hash = location.hash;

            if (hash == '' || hash == '#' || hash == '#/') {
                return '';
            } else {
                if (level == undefined || level == 0) {
                    return hash.split('#')[1];
                } else {
                    var hashSplit = hash.replace('#', '').split('/');

                    return hashSplit.splice(0, level + 1).join('/');
                }

            }
        },
        getHashMaxLevel: function () {
            var hash = location.hash;

            if (hash == '' || hash == '#' || hash == '#/') {
                return 0;
            } else {
                return hash.split('/').length - 1;
            }
        },
        getCmpFromHash: function (level) {
            var hash = this.getLocationHash(level);
            var cmp = {name: 'cmp:Empty', params: {}};

            if (hash) {
                var lastHashSeg = hash.substring(hash.lastIndexOf('/') + 1, hash.length).split('&');

                if (ko.components.isRegistered('cmp:' + lastHashSeg[0])) cmp.name = 'cmp:' + lastHashSeg[0];
                if (lastHashSeg[1]) cmp.params = JSON.uri2JS(lastHashSeg[1]);
            }

            //console.log(cmp);
            return cmp;
        },
        uuid: function (len, radix) {
            var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
            var uuid = [], i;
            radix = radix || chars.length;

            if (len) {
                // Compact form
                for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
            } else {
                // rfc4122, version 4 form
                var r;

                // rfc4122 requires these characters
                uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
                uuid[14] = '4';

                // Fill in random data. At i==19 set the high bits of clock sequence as
                // per rfc4122, sec. 4.1.5
                for (i = 0; i < 36; i++) {
                    if (!uuid[i]) {
                        r = 0 | Math.random() * 16;
                        uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                    }
                }
            }

            return uuid.join('');
        },
        //only extend one time on same object
        extend: function (obj1, obj2) {
            if (obj1._firstExtend == undefined) {
                obj1._firstExtend = true;

                $.extend(obj1, obj2);
            }
        },
        //when the bind value change frequently we can delay the computed callback
        koComputedDelay: function (/*{targetFun:,callback:,timeout:}*/params) {
            if (params) {
                if (!params.timeout) params.timeout = 500;

                ko.computed(function () {
                    var innerScope = this;

                    if (this.callCount > 0) {
                        setTimeout(function () {
                            if (innerScope.lastInputTime != 0 && innerScope.lastInputTime - currentTime == 0) {
                                params.callback();
                            }
                        }, params.timeout);
                    }

                    innerScope.callCount++;
                    innerScope.lastInputTime = new Date().getTime();
                    var currentTime = innerScope.lastInputTime;

                    params.targetFun();
                }, {callCount: 0, lastInputTime: 0});
            }
        },
        delay: function (/*{timeout:,callback:}*/params) {
            if (params) {
                if (!params.timeout) params.timeout = 100;

                setTimeout(function () {
                    params.callback();
                }, params.timeout);
            }

        },
        //for dict like obj translate
        translateData:function (/*{data:,fromName:,fromValue:,toName:}*/params) {
            var tempValue = undefined;
            var tempArray = [];

            if(params.fromValue instanceof Array) {
                tempValue = params.fromValue;
            }
            else {
                tempValue = [params.fromValue]
            }

            tempValue.forEach(function (value) {
              var item = params.data.findItemByField({name:params.fromName,value:value});

              if(item) tempArray.push(item[params.toName]);
            });

            if(tempArray .length == 1){
                return tempArray[0];
            }
            else if(tempArray.length > 1){
                return tempArray;
            }
        },
        gotoTop:function () {
            //if($(window).scrollTop() > 0) {
            $(window).scrollTop(0);
           // }
        }
    };

    $.extend(util, cssUtil);
    $.extend(util, ajaxUtil);
    $.extend(util, componentUtil);
    $.extend(util, commonUtil);

    return util;
});
